package com.jintian.smart.kernel.flowable.engine;

import cn.hutool.core.bean.BeanUtil;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IActivity;
import com.jintian.smart.kernel.core.flow.IFlowRuntime;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.flowable.domain.model.*;
import com.jintian.smart.kernel.flowable.flowable.cmd.ExpressCalcCmd;
import com.jintian.smart.kernel.flowable.flowable.cmd.TransformTaskCmd;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.ManagementService;

import java.util.*;
import java.util.Map.Entry;

@SuppressWarnings("rawtypes")
public class DefaultFlowRuntime implements IFlowRuntime {
    public static final String CONTEXT_PROCESSINSTANCE = "processInstance";
    public static final String CONTEXT_CURRENTTASK = "currentTask";

    private final FlowImpl flow;
    private Object bizBean;
    private final ManagementService managementService;
    private final Map<String, Object> context;

    public DefaultFlowRuntime(FlowImpl flow, ManagementService managementService, Map<String, Object> context) {
        this.flow = flow;
        this.managementService = managementService;
        this.context = context;
    }

    public Object getBizBean() {
        return bizBean;
    }

    public void setBizBean(Object bizBean) {
        this.bizBean = bizBean;
    }

    public Map<String, Object> getContext() {
        return this.context;
    }

    @Override
    public FlowImpl getFlow() {
        return this.flow;
    }

    public List<Object> checkRule(String activity, FlowAction action) throws Exception {
        if (this.flow == null) {
            return Collections.emptyList();
        }
        List<Object> ret = new ArrayList<>();
        ActivityImpl act = this.flow.findActivity(activity);
        if (act == null || act.getBizRules().size() == 0) {
            for (BizRuleImpl rule : this.flow.getBizRules()) {
                if (rule.getExcludeActivitys() != null && rule.getExcludeActivitys().contains(activity)) {
                    continue;
                }
                if (rule.getIncludeActivitys() != null && rule.getIncludeActivitys().contains(activity)) {
                    Object ruleCheck = this.checkRule(rule, action.name());
                    if (ruleCheck != null) {
                        ret.add(ruleCheck);
                    }
                }
            }
        } else {
            for (Object obj : act.getBizRules()) {
                BizRuleImpl rule = null;
                String ruleName = null;
                if (obj instanceof String) {
                    ruleName = (String) obj;
                    rule = this.flow.getBizRule(ruleName);
                } else if (obj instanceof Map) {
                    rule = new BizRuleImpl();
                    BeanUtil.copyProperties(rule, (Map) obj);
                    ruleName = rule.getName();
                    BizRuleImpl orgiRule = this.flow.getBizRule(ruleName);
                    BeanUtil.copyProperties(rule, orgiRule);
                }
                if (rule != null) {
                    Object ruleCheck = this.checkRule(rule, action.name());
                    if (ruleCheck != null) {
                        ret.add(ruleCheck);
                    }
                } else {
                    throw new IllegalArgumentException("不存在名为" + ruleName + "的业务规则");
                }
            }
        }
        return ret;
    }

    private Object checkRule(BizRuleImpl rule, String event) {
        if (StringUtil.isNotBlank(rule.getEnable())) {
            if (!this.hasTrueCondition("业务规则" + rule.getName() + "生效条件", rule.getEnable())) {
                return null;
            }
        }
        if (rule.getExcludeEvents() != null && rule.getExcludeEvents().contains(event)) {
            return null;
        }
        if (rule.getIncludeEvents() != null && rule.getIncludeEvents().size() > 0) {
            // 如果指定了生效的事件
            if (!rule.getIncludeEvents().contains(event)) {
                return null;
            }
        }
        if (this.hasTrueCondition("业务规则" + rule.getName() + "判定条件", rule.getCondition())) {
            Map<String, Object> ruleMsg = new HashMap<>();
            ruleMsg.put("name", rule.getName());
            ruleMsg.put("message", rule.getMessage());
            ruleMsg.put("stop", rule.isStop());
            return this.createRuleMessage(rule.getName(), rule.getMessage(), rule.isStop());
        }
        return null;
    }

    public Object createRuleMessage(String name, String message, boolean stop) {
        Map<String, Object> ruleMsg = new HashMap<>();
        ruleMsg.put("name", name);
        ruleMsg.put("message", message);
        ruleMsg.put("stop", stop);
        return ruleMsg;
    }

    public boolean existStopRule(List<Object> checkResult) {
        if (checkResult.size() == 0) {
            return false;
        }
        for (Object obj : checkResult) {
            if (Boolean.TRUE.equals(((Map) obj).get("stop"))) {
                return true;
            }
        }
        return false;
    }

    public String getStopMessage(List<Object> checkResult) {
        if (checkResult.size() == 0) {
            return null;
        }
        StringBuffer sb = new StringBuffer();
        for (Object obj : checkResult) {
            if (Boolean.TRUE.equals(((Map) obj).get("stop"))) {
                sb.append(((Map) obj).get("message"));
            }
        }
        return sb.toString();
    }

    public Object calculate(String expr) {
        return ExpressCalcCmd.calculate(managementService, expr, context);
    }

    public <T> T calculate(String expr, Class<T> checkClazz, String castError) {
        Object ret = ExpressCalcCmd.calculate(managementService, expr, context);
        if (ret == null)
            return null;
        if (!checkClazz.isInstance(ret)) {
            throw new FlowableException(castError);
        }
        return checkClazz.cast(ret);
    }

    public boolean canFlowTo(String currentNodeId, String targetNodeId) {
        if (this.flow == null) {
            return true;
        }
        for (ActivityImpl activityImpl : this.flow.getActvities()) {
            if (currentNodeId.equals(activityImpl.getName())) {
                for (FlowRuleImpl rule : activityImpl.getFlowRules()) {
                    if (targetNodeId.equals(rule.getToActivity())) {
                        return hasTrueCondition("环节(" + currentNodeId + ")流转规则(to=" + rule.getToActivity() + ")", rule.getCondition());
                    }
                }
                break;
            }
        }
        return true;
    }

    public boolean hasTrueCondition(String what, String conditionExpression) {
        Object result = this.calculate(conditionExpression);
        return vaildBoolean(what, conditionExpression, result);
    }

    private boolean vaildBoolean(String what, String expr, Object result) {
        if (result == null) {
            throw new FlowableException(what + "条件表达式" + expr + "返回null");
        }
        if (!(result instanceof Boolean)) {
            throw new FlowableException(what + "条件表达式" + expr + "返回非布尔类型");
        }
        return ((Boolean) result).booleanValue();
    }

    /**
     * 应用流程变量到上下文中
     */
    public Map<String, Object> applyProcessVariablesToContext() {
        if (flow == null) {
            return Collections.emptyMap();
        }
        // 计算流程变量
        Map<String, Object> ret = new HashMap<>(flow.getContextVariables().size());
        Map<String, String> variables = flow.getContextVariables();
        for (Entry<String, String> e : variables.entrySet()) {
            if (!context.containsKey(e.getKey())) {
                if (ExpressCalcCmd.isExpression(e.getValue())) {
                    context.put(e.getKey(), this.calculate(e.getValue()));
                } else {
                    context.put(e.getKey(), e.getValue());
                }
            }
            ret.put(e.getKey(), context.get(e.getKey()));
        }
        return ret;
    }

    @SuppressWarnings("unchecked")
    public void calcExecutorRules(ProcessControl.ProcessControlItem flowTo) {
        if (this.flow == null) {
            return;
        }
        ActivityImpl activityImpl = this.flow.findActivity(flowTo.getActivity());
        // 未配置
        if (activityImpl == null) {
            return;
        }
        for (ExecuteRuleImpl rule : activityImpl.getExecuteRules()) {
            if (StringUtil.isBlank(rule.getCondition()) || this.hasTrueCondition("环节(" + flowTo.getActivity() + ")执行规则生效条件", rule.getCondition())) {
                if (StringUtil.isNotBlank(rule.getExecutors())) {
                    List result = this.calculate(rule.getExecutors(), List.class, "环节(" + flowTo.getActivity() + ")默认执行者表达式" + rule.getExecutors() + "返回非List类型");
                    if (result != null) {
                        flowTo.getExecutors().addAll(result);
                    }
                }
                if (StringUtil.isNotBlank(rule.getExecutorRange())) {
                    List result = this.calculate(rule.getExecutorRange(), List.class, "环节(" + flowTo.getActivity() + ")执行者范围表达式" + rule.getExecutors() + "返回非List类型");
                    if (result != null) {
                        flowTo.getExecutorRange().addAll(result);
                    }
                }
                // 第一个生效的
                return;
            }
        }
    }

    public void calcTransformRules(ProcessControl.ProcessControlItem flowTo) {
        if (this.flow == null) {
            return;
        }
        ActivityImpl activityImpl = this.flow.findActivity(flowTo.getActivity());
        // 未配置
        if (activityImpl == null) {
            return;
        }
        for (TransformRuleImpl rule : activityImpl.getTransformRules()) {
            if (StringUtil.isBlank(rule.getCondition()) || this.hasTrueCondition("环节(" + flowTo.getActivity() + ")执行规则生效条件", rule.getCondition())) {
                if (StringUtil.isNotBlank(rule.getExecutors())) {
                    List result = this.calculate(rule.getExecutors(), List.class, "环节(" + flowTo.getActivity() + ")默认执行者表达式" + rule.getExecutors() + "返回非List类型");
                    if (result != null) {
                        flowTo.getExecutors().addAll(result);
                    }
                }
                if (StringUtil.isNotBlank(rule.getExecutorRange())) {
                    List result = this.calculate(rule.getExecutorRange(), List.class, "环节(" + flowTo.getActivity() + ")执行者范围表达式" + rule.getExecutors() + "返回非List类型");
                    if (result != null) {
                        flowTo.getExecutorRange().addAll(result);
                    }
                }
                flowTo.getVariables().put(TransformTaskCmd.FLAG_TRANSFORM, rule.isBackToOwner());
                // 第一个生效的
                return;
            }
        }
    }

    @Override
    public Map<String, Object> getTaskVariables(String activity) {
        Map<String, Object> variables = new HashMap<>();
        if (this.flow == null) {
            return variables;
        }
        ActivityImpl activityImpl = this.flow.findActivity(activity);
        // 未配置
        if (activityImpl == null) {
            return variables;
        }
        Map<String, String> taskAttributes = new HashMap<>(this.flow.getTaskAttributes());
        taskAttributes.putAll(activityImpl.getTaskAttributes());
        for (Entry<String, String> e : taskAttributes.entrySet()) {
            Object value = ExpressCalcCmd.calculate(managementService, e.getValue(), context);
            variables.put(e.getKey(), value);
        }
        return variables;
    }

    /**
     * GlobalFlowableEventListener中读取启动规则，设置process持久变量
     */
    @Override
    public Map<String, Object> getProcessVariables() {
        Map<String, Object> variables = new HashMap<>();
        if (this.flow == null) {
            return variables;
        }
        for (StartRuleImpl rule : this.flow.getStartRules()) {
            if (StringUtil.isBlank(rule.getCondition()) || this.hasTrueCondition("启动规则生效条件", rule.getCondition())) {
                // 添加流程变量表
                for (Entry<String, String> e : rule.getProcessAttributes().entrySet()) {
                    Object value = ExpressCalcCmd.calculate(managementService, e.getValue(), context);
                    variables.put(e.getKey(), value);
                }
                return variables;
            }
        }
        return variables;
    }

    @Override
    public String getActivityFormUrl(String name) {
        IActivity act = this.flow.findActivity(name);
        if (act != null) {
            return act.getFormUrl();
        } else {
            return this.flow.getFormUrl();
        }
    }
}
